module ActiveBackground
  module DatabaseHandler
    def database(attempt=0, &block)
      database = ActiveBackground::Database.instance
      database.lock
	  # TODO: Optimize code and remove double checks
      begin
        db = Thread.new do
          begin
            database.prepare
            self.instance_eval(&block)
            database.terminate
          rescue Exception => e
            @logger.error("\n******************************************\n" +
                          "Exception: #{e.to_s} - (#{e.class})\n" +
                          "Database: Failed connecting to database " + (attempt<=10?"(attempt: #{attempt})\n":"Action killed\n") +
                          "----------------BACKTRACE-----------------\n" +
                                    e.backtrace.join("\n") + "\n" +
                          "-------------------END--------------------\n" +
                          "\n******************************************\n" )
            @logger.error("Database: Action failed with #{block.inspect}") unless attempt<=10
            database(attempt+1, &block) if attempt <=10
          end
        end
        db.join
      rescue
        if attempt <=10
          database(attempt+1, &block)
          @logger.debug "Database: Failed to complete task, attempting (#{attempt}) again"
        else
          raise 'Database: Could not complete database task for job ' + key
          @logger.warn 'Database: Could not complete database task for job ' + key
        end
      end
      database.unlock
    end
  end
  
  class Database
    include Singleton
    
    # setup of the database is extracted from the Configuration instance where
    # its retrieved from either the database or rails.
    def setup
      @locks = 0
      @maximum = 50
      @queue = 0
      @db = Mutex.new
      @setup = false
      
      unless @setup
        @config = ActiveBackground::Configuration.instance
        @logger = @config.logger
        case @config.database.lib_type
          when :active_record
            # FIXME: need to get this to work outside of rails environment
            ActiveRecord::Base.allow_concurrency = true
            ActiveRecord::Base.establish_connection(@config.database.connect_hash)
        end
        @setup = true
      end
    end
    
    def lock
      # TODO: need to be able to lock until database actions drop below certain point
      # lets put the thing into queue
      @db.synchronize do @queue += 1 end
      # wait till there are locks available
      while @maximum <= @locks
        sleep 1
        @logger.info "Database: Waiting for db lock.... (Currently there are #{@locks} db actions running and #{@queue} in queue)"
      end
      # some info
      @logger.info "Database: Currently there are #{@locks+1} db actions running and #{@queue-1} in queue."
      # increase lock and decrease queue
      @db.synchronize do 
        @queue -= 1
        @locks += 1
      end
    end
    
    def unlock
      # remove the lock
      @db.synchronize do
        @locks -= 1
      end
    end
    
    def prepare
      @config = ActiveBackground::Configuration.instance
      case @config.database.lib_type
        when :active_record
          ActiveRecord::Base.connection
          results = ActiveRecord::Base.connection.execute("SHOW PROCESSLIST;")
          c=0
          results.each do |connection|
            c+=1
          end
          ACTIVE_BACKGROUND_LOGGER.debug("Database: Currently running processes: #{c}")
      end
    end
    
    def terminate
      @config = ActiveBackground::Configuration.instance
      case @config.database.lib_type
        when :active_record
          ActiveRecord::Base.connection.disconnect!
      end
    end
  end
end